//
// Created by ly-zhouxyi on 2024/3/4.
//

#include "FFmpegCallJava.h"


FFmpegCallJava::FFmpegCallJava(_JavaVM *javaVM, JNIEnv *env, jobject *obj) {

    this->javaVM = javaVM;
    this->jniEnv = env;
    this->jobj = *obj;
    this->jobj = env->NewGlobalRef(jobj);


    jclass clazz = jniEnv->GetObjectClass(jobj);
    if (!clazz) {
        if (LOG_DEBUG) {
            LOGE("get clazz wrong");
        }
        return;
    }
    methodId_prepare = jniEnv->GetMethodID(clazz, "onPrepare", "()V");
    methodId_load = jniEnv->GetMethodID(clazz, "onLoad", "(Z)V");
    methodId_playing = jniEnv->GetMethodID(clazz, "onPlaying", "(Z)V");
    methodId_renderYUV = jniEnv->GetMethodID(clazz, "onRenderYUV", "(II[B[B[B)V");
    methodId_timeInfo = jniEnv->GetMethodID(clazz, "onTimeInfo", "(II)V");
}

void FFmpegCallJava::onPrepare() {
    JNIEnv *env;
    int state= (*javaVM).GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    if ( state== JNI_EDETACHED) {
        // 子线程 获取不到 jniEnv 所以需要通过jvm 获取jniEnv
        if (javaVM->AttachCurrentThread(&env, 0) != JNI_OK) {
            return;
        }
        env->CallVoidMethod(jobj, methodId_prepare);
        javaVM->DetachCurrentThread();
    } else{
        env->CallVoidMethod(jobj, methodId_prepare);
    }

}


void FFmpegCallJava::onRenderYUV(int width, int height, uint8_t *fy, uint8_t *fu, uint8_t *fv) {
    JNIEnv *jniEnv;
    if (javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) {
        if (LOG_DEBUG) {
            LOGE("call onPrepare error");
        }
        return;
    }
    jbyteArray y = jniEnv->NewByteArray(width * height);
    jniEnv->SetByteArrayRegion(y, 0, width * height, reinterpret_cast<const jbyte *>(fy));

    jbyteArray u = jniEnv->NewByteArray(width * height / 4);
    jniEnv->SetByteArrayRegion(u, 0, width * height / 4, reinterpret_cast<const jbyte *>(fu));

    jbyteArray v = jniEnv->NewByteArray(width * height / 4);
    jniEnv->SetByteArrayRegion(v, 0, width * height / 4, reinterpret_cast<const jbyte *>(fv));

    jniEnv->CallVoidMethod(jobj, methodId_renderYUV, width, height, y, u, v);
    jniEnv->DeleteLocalRef(y);
    jniEnv->DeleteLocalRef(u);
    jniEnv->DeleteLocalRef(v);
    javaVM->DetachCurrentThread();
}

void FFmpegCallJava::onTimeInfo(int curr, int total) {
    JNIEnv *env;
    int state= (*javaVM).GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    if ( state== JNI_EDETACHED) {
        if (javaVM->AttachCurrentThread(&env, 0) != JNI_OK) {
            return;
        }
        env->CallVoidMethod(jobj, methodId_timeInfo, curr, total);
        javaVM->DetachCurrentThread();
    } else{
        env->CallVoidMethod(jobj, methodId_timeInfo, curr, total);
    }
}

void FFmpegCallJava::onLoad(bool load) {
    JNIEnv *env;
    int state= (*javaVM).GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    if ( state== JNI_EDETACHED) {
        if (javaVM->AttachCurrentThread(&env, 0) != JNI_OK) {
            return;
        }
        env->CallVoidMethod(jobj, methodId_load, load);
        javaVM->DetachCurrentThread();
    } else{
        env->CallVoidMethod(jobj, methodId_load, load);
    }
}

void FFmpegCallJava::onPlaying(bool isPlaying) {
    JNIEnv *env;
    int state= (*javaVM).GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    if ( state== JNI_EDETACHED) {
        if (javaVM->AttachCurrentThread(&env, 0) != JNI_OK) {
            return;
        }
        env->CallVoidMethod(jobj, methodId_playing, isPlaying);
        javaVM->DetachCurrentThread();
    } else{
        env->CallVoidMethod(jobj, methodId_playing, isPlaying);
    }
}

FFmpegCallJava::~FFmpegCallJava() {

}

